
#Region "Imports"
Imports Microsoft.Win32
Imports System

Imports System.IO
Imports ICSharpCode.SharpZipLib.Core
Imports ICSharpCode.SharpZipLib.Zip
#End Region

' see what to do with begin - do - commit

Public Class MainDlg

#Region "Data"

    ' objects used by the class while running
    Public Shared thWork As System.Threading.Thread = Nothing
    Public Shared strmZipOutputStream As ZipOutputStream = Nothing
    Public Shared regAppKey As RegistryKey = Nothing
    Public Shared strmFNfo As System.IO.StreamWriter = Nothing
    Public Shared strmFNfoIn As System.IO.StreamReader = Nothing
    Public Shared zf As ICSharpCode.SharpZipLib.Zip.ZipFile = Nothing
    Public Shared bBrw As Browse = Nothing


    ' these are the main options that gowern the action made by the app
    Private Const iUpdCnt As Integer = 1000 ' number of files after which update takes place
    Private Shared iDoEvTime As Long
    Private Shared iCompressLvl As Integer
    Private Shared sBaseNm As String = ""
    Private Shared sPathCl As String = ""
    Private Shared sOutCl As String = ""
    Public Shared sExclLst() As String = _
        {"TEMP", "RECYCLER", "SYSTEM VOLUME INFORMATION", "$AVG", "TRAPEZ", "POZE"}


    ' run time overrides
    Private Shared bSkipFolder As Boolean ' shall skip current folder
    Private Const sFileToSkip As String = "SkipThisFolder.nfo"
    Private Shared sPthFSkip As String
    Public Shared iSts As Integer = 0 ' the status of the application
        ' 0 - idle
        ' 1 - running
        ' 2 - request to terminate
        ' 3 - request to pause / paused


    ' run time statistics
    Private Shared LvlDeep As Integer = 0
    Private Shared dateSt As Date ' the moment of start
    Private Shared lAddedBytes As Int64 ' bytes count (real, not compressed)
    Private Shared iTyBytes As Integer ' type of display (0 - bytes, 1 - KB, 2 - MB)



    Private Shared abyBuffer() As Byte ' buffer used in transferring file content to zip

    Private Shared sUpdLn_P As String = ""
    Private Shared sUpdLn_N As String = ""
    Private Shared dateRefUpd As Date ' the moment of last update uppon the zip

    Private Shared iItemsAdded As Integer
    Private sCrtMode As String = ""

#End Region


#Region "Form Events"

    Private Sub MainDlg_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        regAppKey.Close()
    End Sub

    Private Sub MainDlg_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        If Not thWork Is Nothing Then
        If thWork.ThreadState = Threading.ThreadState.Running Then
            iSts = 2
            Me.txLog.AppendText(vbNewLine & vbNewLine & "Can't quit while working! Cancelling ..." & vbNewLine & vbNewLine)
             e.Cancel = True
             Return
            End If
        End If

        UTIL.SaveStg("MDLG", "Win_l", Me.Left)
        UTIL.SaveStg("MDLG", "Win_t", Me.Top)
        UTIL.SaveStg("MDLG", "Win_w", Me.Width)
        UTIL.SaveStg("MDLG", "Win_h", Me.Height)
        UTIL.SaveStg("MDLG", "In", Me.txIn.Text)
        UTIL.SaveStg("MDLG", "Out", Me.txOut.Text)
        UTIL.SaveStg("MDLG", "Strip", Me.txStrip.Text)

        If UBound(sExclLst) = -1 Then
            UTIL.SaveStg("BLACKL", "0", "")
        Else
            Dim i As Integer
            For i = 0 To UBound(sExclLst)
                UTIL.SaveStg("BLACKL", i.ToString, sExclLst(i))
            Next
            UTIL.SaveStg("BLACKL", i.ToString, "")
        End If

        Dim lLng As Long = 0
        Try
            lLng = CLng(Me.txIdlePics.Text)
        Finally
        End Try
        If lLng < 0 Then
            lLng = 0
        ElseIf lLng > 10 Then
            lLng = 10
        End If
        UTIL.SaveStg("MDLG", "Idle", lLng)
        UTIL.SaveStg("MDLG", "CpLvl", Me.cbLvlCp.SelectedIndex)

    End Sub

    Private Sub MainDlg_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim regKey As RegistryKey

        Try
            regAppKey = Registry.LocalMachine.OpenSubKey("Software\CpZp", True)
        Finally
            If regAppKey Is Nothing Then
                regKey = Registry.LocalMachine.OpenSubKey("SOFTWARE", True)
                regKey.CreateSubKey("CpZp")
                regKey.Close()
                regAppKey = Registry.LocalMachine.OpenSubKey("Software\CpZp", True)
            End If
        End Try
        Me.Left = UTIL.GetLongStg("MDLG", "Win_l", 150)
        Me.Top = UTIL.GetLongStg("MDLG", "Win_t", 100)
        Me.Width = UTIL.GetLongStg("MDLG", "Win_w", 338)
        Me.Height = UTIL.GetLongStg("MDLG", "Win_h", 230)

        Me.txIn.Text = UTIL.GetStrStg("MDLG", "In", "")
        Me.txOut.Text = UTIL.GetStrStg("MDLG", "Out", "")
        Me.txStrip.Text = UTIL.GetStrStg("MDLG", "Strip", "")
        Me.txIdlePics.Text = UTIL.GetLongStg("MDLG", "Idle", 0)
        Me.cbLvlCp.SelectedIndex = UTIL.GetLongStg("MDLG", "CpLvl", 8)

        Dim i As Integer = 0
        Dim sOut As String
        ReDim sExclLst(-1)
        Do
            sOut = UTIL.GetStrStg("BLACKL", i.ToString, "")
            If sOut = "" Then Exit Do
            If i = 0 Then
                ReDim sExclLst(0 To 0)
                sExclLst(0) = sOut
            Else
                ReDim Preserve sExclLst(0 To i)
                sExclLst(i) = sOut
            End If
            i = i + 1
        Loop

    End Sub

    Private Sub bBrwOut_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bBrwOut.Click
        Dim fdlg As Windows.Forms.SaveFileDialog = New Windows.Forms.SaveFileDialog

        fdlg.Title = "Selectati locatia si numele"
        fdlg.InitialDirectory = ""
        fdlg.Filter = "Zip Files (*.zip)|*.zip|All files (*.*)|*.*"
        fdlg.FileName = Me.txOut.Text
        fdlg.RestoreDirectory = False
        fdlg.CheckPathExists = True
        fdlg.DefaultExt = "zip"
        fdlg.OverwritePrompt = True
        If fdlg.ShowDialog() = Windows.Forms.DialogResult.OK Then
            Me.txOut.Text = fdlg.FileName
        End If

    End Sub

    Private Sub bBrwIn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bBrwIn.Click
        ' First create a FolderBrowserDialog object
        Dim FolderBrowserDialog1 As New FolderBrowserDialog
        ' Then use the following code to create the Dialog window
        ' Change the .SelectedPath property to the default location
        With FolderBrowserDialog1
            ' Desktop is the root folder in the dialog.
            .RootFolder = Environment.SpecialFolder.Desktop
            ' Select the directory on entry.
            .SelectedPath = Me.txIn.Text
            ' Prompt the user with a custom message.
            .Description = "Select the input directory"
            If .ShowDialog = Windows.Forms.DialogResult.OK Then
                ' Display the selected folder if the user clicked on the OK button.
                Me.txIn.Text = .SelectedPath
            End If
        End With
    End Sub

    Private Sub bGoStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bGoStop.Click

        If iSts = 0 Then ' if idle
             ' go build / update

            ' aquire input values
            sPathCl = Me.txIn.Text
            sOutCl = Me.txOut.Text
            Try
                iDoEvTime = CLng(Me.txIdlePics.Text)
            Catch ex As Exception
                MsgBox("The value for idle cycles is non-numeric", MsgBoxStyle.ApplicationModal Or MsgBoxStyle.Information)
                Return
            End Try
            If System.IO.Directory.Exists(sPathCl) = False Then
                MsgBox("Specified folder for input does not exists", MsgBoxStyle.ApplicationModal Or MsgBoxStyle.Information)
                Return
            End If
            iCompressLvl = Me.cbLvlCp.SelectedIndex + 1
            sBaseNm = Me.txStrip.Text
            If (sBaseNm <> "") Then
                If Not sBaseNm Like "*\" Then
                    sBaseNm = sBaseNm & "\"
                End If
                If (Me.txIn.Text.IndexOf(sBaseNm) <> 0) Then
                    MsgBox("The strip must be part of input path", MsgBoxStyle.ApplicationModal Or MsgBoxStyle.Information)
                    Return
                End If
            End If


            ' general preparations for run
            Me.bGoStop.Text = "Stop"
            LogText("Starting the process ..." & vbNewLine)
            LockForRun(True)

            dateSt = DateTime.Now
            Me.tslStartTime.Text = dateSt.ToString("hh:mm:ss")
            lAddedBytes = 0
            iTyBytes = 0
            Me.tbsByCntLbl.Text = "Bytes added:"
            Me.tsProgress.Minimum = 0


            iSts = 1

           ' the run
            If System.IO.File.Exists(sOutCl) = True Then
                ' the syncronus solution for this:
                'UpdateZip()
                'DoTheFinish
                If (System.IO.File.Exists(sOutCl & ".nfo")) Then
                    sCrtMode = "Updating"
                    ChgSts(sCrtMode)
                    thWork = New System.Threading.Thread(AddressOf UpdateZip_Async)
                Else
                    LogText("ERROR!!! The .nfo file is missing!")
                    DoTheFinish()
                    iSts = 0
                    Return
                End If
            Else
                ' the syncronus solution for this:
                'BuildNewZip()
                'DoTheFinish
                sCrtMode = "Creating"
                ChgSts(sCrtMode)
                thWork = New System.Threading.Thread(AddressOf BuildNewZip_Async)
            End If
            thWork.Start()


        Else ' stop now ...
            LogText("Stopped. Waiting acknowledge ..." & vbNewLine)
            Me.bGoStop.Enabled = False
            Me.bGoStop.Text = "Canceling ..."
            ChgSts("Stopping...")
            iSts = 2
        End If
    End Sub

    Private Sub bPause_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bPause.Click
        If iSts <> 3 Then
            ChgSts("Pausing...")
            LogText("Paused. Waiting acknowledge ...")
            Me.bPause.Text = "Resume"
            iSts = 3
        Else
            ChgSts("Resuming...")
            LogText("Resuming ..")
            iSts = 1
        End If
    End Sub

    Private Sub bSkipFile_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bSkipFile.Click
        LogText("Action is not implemented" & vbNewLine)
    End Sub

    Private Sub bSkipDir_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bSkipDir.Click
        ChgSts("Skipping...")
        bSkipFolder = True
    End Sub

    Private Sub TrayNtf_MouseDoubleClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles TrayNtf.MouseDoubleClick
        If Me.WindowState = FormWindowState.Minimized Then
            Me.Visible = True
            Me.WindowState = FormWindowState.Normal
            Me.BringToFront()
            If Not bBrw Is Nothing Then
                bBrw.Visible = True
                bBrw.BringToFront()
            End If
        Else
            Me.WindowState = FormWindowState.Minimized
            Me.Visible = False
            If Not bBrw Is Nothing Then
                bBrw.Visible = False
            End If
        End If
    End Sub

    Private Sub MainDlg_Resize(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Resize
        If Me.WindowState = FormWindowState.Minimized Then
            Me.Visible = False
            If Not bBrw Is Nothing Then
                bBrw.Visible = False
            End If
        End If
    End Sub

    Private Sub bFldCk_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bFldCk.Click
        If bBrw Is Nothing Then
            bBrw = Browse.Display(Me.txIn.Text)
        Else
            bBrw.Reinit(Me.txIn.Text)
        End If
    End Sub

    Private Sub txIn_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txIn.TextChanged
        If System.IO.Directory.Exists(Me.txIn.Text) Then
            If Not bBrw Is Nothing Then
                If bBrw.Visible = True Then
                    bBrw.Reinit(Me.txIn.Text)
                End If
            End If
        End If
    End Sub

    Private Sub bBlackList_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bBlackList.Click
        Dim a As New ExclList
        a.ShowDialog()
    End Sub

#End Region


#Region "Delegates"

    Private Delegate Sub DelDoLogText(ByVal sTxt As String)
    Private LogText As New DelDoLogText(AddressOf DoLogText)
    Private Sub DoLogText(ByVal sTxt As String)
        Try
        If Me.InvokeRequired Then
            Dim d As New DelDoLogText(AddressOf DoLogText)
            Dim aLst(0 To 0) As Object
            aLst(0) = sTxt
            Me.Invoke(d, aLst)
        Else
            Me.txLog.AppendText(sTxt)
            If Me.txLog.Lines.Length > 1350 Then
                Dim txStr As String
                Dim i As Integer
                Dim j As Integer = txLog.Lines.Length - 2
                txStr = txLog.Lines(j) & vbNewLine
                For i = j - 1 To j - 1300 Step -1
                    txStr = txLog.Lines(i) & vbNewLine & txStr
                Next
                Me.txLog.Text = txStr
           End If

        End If
        Finally
        End Try
    End Sub



    Private Delegate Sub DelChgSts(ByVal sTxt As String)
    Private ChgSts As New DelDoLogText(AddressOf DoChgSts)
    Private Sub DoChgSts(ByVal sTxt As String)
        Try
        If Me.InvokeRequired Then
            Dim d As New DelChgSts(AddressOf DoChgSts)
            Dim aLst(0 To 0) As Object
            aLst(0) = sTxt
            Me.Invoke(d, aLst)
        Else
            Me.tslSts.Text = sTxt
        End If
        Catch ex As System.Exception
            DoException(ex)
        End Try
    End Sub



    Private Delegate Sub DelTheFinish()
    Private TheFinish As New DelTheFinish(AddressOf DoTheFinish)
    Private Sub DoTheFinish()
        Try
            If Me.InvokeRequired Then
                Dim d As New DelTheFinish(AddressOf DoTheFinish)
                Me.Invoke(d)
            Else
                'Me.Visible = False
                MsgBox("Process ended.", MsgBoxStyle.ApplicationModal Or MsgBoxStyle.Information)
                'Me.Visible = True
                Me.bGoStop.Text = "Go"
                iSts = 0
                LogText("The process has ended" & vbNewLine)
                LockForRun(False)
                Me.bGoStop.Enabled = True
                ChgSts("Idle")
            End If
        Catch ex As System.Exception
            DoException(ex)
        End Try
    End Sub



    Private Delegate Sub DelTheProgInit(ByVal lMax As Long)
    Private TheProgInit As New DelTheProgInit(AddressOf DoTheProgInit)
    Private Sub DoTheProgInit(ByVal lMax As Long)

        Try
            If Me.InvokeRequired Then
                Dim d As New DelTheProgInit(AddressOf DoTheProgInit)
                Dim aLst(0 To 0) As Object
                aLst(0) = lMax
                Me.Invoke(d, aLst)
            Else
                Me.tsProgress.Maximum = lMax
                Me.tsProgress.Value = 0
            End If
        Catch ex As System.Exception
            DoException(ex)
        End Try
    End Sub



    Private Delegate Sub DelTheStep()
    Private TheStep As New DelTheStep(AddressOf DoTheStep)
    Private Sub DoTheStep()
        Try
            If Me.InvokeRequired Then
                Dim d As New DelTheStep(AddressOf DoTheStep)
                Me.Invoke(d)
            Else
                If Me.tsProgress.Value < Me.tsProgress.Maximum Then
                    Me.tsProgress.Value = Me.tsProgress.Value + 1
                End If
            End If
        Catch ex As System.Exception
            DoException(ex)
        End Try
    End Sub



    Private Delegate Sub DelTheValStep(ByVal lNewVal As Long)
    Private TheValStep As New DelTheValStep(AddressOf DoTheValStep)
    Private Sub DoTheValStep(ByVal lNewVal As Long)
        Try
            If Me.InvokeRequired Then
                Dim d As New DelTheValStep(AddressOf DoTheValStep)
                Dim aLst(0 To 0) As Object
                aLst(0) = lNewVal
                Me.Invoke(d, aLst)
            Else
                If lNewVal < Me.tsProgress.Maximum Then
                    Me.tsProgress.Value = lNewVal
                Else
                    Me.tsProgress.Value = tsProgress.Maximum
                End If
            End If
        Catch ex As System.Exception
            DoException(ex)
        End Try
    End Sub



    Private Delegate Sub DelTheReinit(ByVal lTmptOT As Long, ByVal lTmpv As Long)
    Private TheReinit As New DelTheReinit(AddressOf DoTheReinit)
    Private Sub DoTheReinit(ByVal lTmptOT As Long, ByVal lTmpv As Long)
        Try
            If Me.InvokeRequired Then
                Dim d As New DelTheReinit(AddressOf DoTheReinit)
                Me.Invoke(d, lTmptOT, lTmpv)
            Else
                Me.tsProgress.Maximum = lTmptOT
                Me.tsProgress.Value = lTmpv
            End If
        Catch ex As System.Exception
            DoException(ex)
        End Try
    End Sub



    Private Delegate Sub DelOnEachAddedItem()
    Private OnEachAddedItem As New DelOnEachAddedItem(AddressOf DoOnEachAddedItem)
    Private Sub DoOnEachAddedItem()
        Try
            If Me.InvokeRequired Then
                Dim d As New DelOnEachAddedItem(AddressOf DoOnEachAddedItem)
                Me.Invoke(d)
            Else

                If lAddedBytes < 10000 Then
                    Me.tsbByCnt.Text = lAddedBytes.ToString
                ElseIf lAddedBytes < 1024 * 2 Then
                    If (iTyBytes <> 1) Then
                        Me.tbsByCntLbl.Text = "KB added:"
                        iTyBytes = 1
                    End If
                    Me.tsbByCnt.Text = (lAddedBytes / 1024).ToString("0")
                Else 'If lAddedBytes < 1024 * 1024 * 2 Then
                    If (iTyBytes <> 2) Then
                        Me.tbsByCntLbl.Text = "MB added:"
                        iTyBytes = 2
                    End If
                    Me.tsbByCnt.Text = (lAddedBytes / (1024 * 1024)).ToString("0")
                End If

                Dim dateX As System.TimeSpan
                dateX = DateTime.Now - dateSt
                Me.tslElpsT.Text = dateX.ToString().Substring(0, 8)
                If iDoEvTime <> 0 Then
                    System.Threading.Thread.Sleep(iDoEvTime)
                End If

            End If
        Catch ex As System.Exception
            DoException(ex)
        End Try
    End Sub



    Private Delegate Sub DelInPause()
    Private InPause_C As New DelInPause(AddressOf DoInPause)
    Private Sub DoInPause()
        Try
            If Me.InvokeRequired Then
                Dim d As New DelInPause(AddressOf DoInPause)
                Me.Invoke(d)
            Else
                Me.bPause.Text = "Pause"
            End If
        Catch ex As System.Exception
            DoException(ex)
        End Try
    End Sub



    Private Delegate Sub DelInPause1()
    Private InPause_C1 As New DelInPause(AddressOf DoInPause1)
    Private Sub DoInPause1()
       Try
            If Me.InvokeRequired Then
                Dim d As New DelInPause1(AddressOf DoInPause1)
                Me.Invoke(d)
            Else
                MsgBox("Process paused. Press Resume when ready", _
                    MsgBoxStyle.ApplicationModal Or MsgBoxStyle.Information)
            End If
        Catch ex As System.Exception
            DoException(ex)
        End Try
    End Sub



    Private Delegate Sub DelTreeSel(ByVal aIt As TreeNode)
    Private TreeSel As New DelTreeSel(AddressOf DoTreeSel)
    Private Sub DoTreeSel(ByVal aIt As TreeNode)
       Try
            If Me.InvokeRequired Then
                Dim d As New DelTreeSel(AddressOf DoTreeSel)
                Me.Invoke(d, aIt)
            Else
                aIt.TreeView.SelectedNode = aIt
                aIt.EnsureVisible()
            End If
        Catch ex As System.Exception
            DoException(ex)
        End Try
    End Sub


#End Region


#Region "Effective building (creating new zip)"


    ' may be called as Thread new
    Private Sub BuildNewZip_Async()
        BuildNewZip()
        TheFinish()
    End Sub


    ' based on input and output paths, it builds a new zip
    Private Function BuildNewZip() As Boolean
        ReDim abyBuffer(0 To 4095)

        Try
            REM Compression Level: 0-9
            REM 0: no(Compression)
            REM 9: maximum compression

            sPthFSkip = Microsoft.VisualBasic.FileIO.SpecialDirectories.Temp & "\" & sFileToSkip
            strmFNfo = New System.IO.StreamWriter(sPthFSkip, False)
            strmFNfo.Close()

            strmFNfo = New System.IO.StreamWriter(sOutCl & ".nfo", False)
            strmZipOutputStream = New ZipOutputStream(File.Create(sOutCl))
            strmZipOutputStream.SetLevel(iCompressLvl)

            If bBrw Is Nothing Then
                BuildAPath(sPathCl)
            Else
                BuildTreeItemRoot()
            End If

            strmZipOutputStream.Finish()
            System.IO.File.Delete(sPthFSkip)

        Catch ex As System.Exception
            DoException(ex)
        Finally
            If Not strmZipOutputStream Is Nothing Then strmZipOutputStream.Close()
            If Not strmFNfo Is Nothing Then strmFNfo.Close()
        End Try
        ReDim abyBuffer(0 To 4)

    End Function


    ' called when tree mode is active as root function
    Private Sub BuildTreeItemRoot()
        Dim root As TreeNode = bBrw.TreeView.Nodes.Item(0)

        For Each aIt As TreeNode In root.Nodes
            If aIt.Checked = True Then
                BuildTreeItem(aIt)
            Else
                BuildSkipFolder(Browse.GetNodePath(aIt))
            End If
        Next

    End Sub


    ' each effective item in tree gets passed to this function (folders and files)
    Private Sub BuildTreeItem(ByRef aIt As TreeNode)
        Dim sPth As String = Browse.GetNodePath(aIt)
        Dim sInZName As String
        Dim lTmptOT As Long

        TreeSel(aIt)
        If (aIt.Tag = "N") Then
            ' this is an folder that has not been expanded
            BuildAPath(sPth)
        ElseIf (aIt.Tag = "E") Then
            lTmptOT = aIt.Nodes.Count
            ' progress bar indicates this folder
            TheProgInit(lTmptOT)
            ' this is an folder that has been expanded
            For Each aIt1 As TreeNode In aIt.Nodes
                TreeSel(aIt1)
                TheStep()
                If aIt.Checked = True Then
                    Dim lTmpv As Long = Me.tsProgress.Value
                    BuildTreeItem(aIt1)
                    TheReinit(lTmptOT, lTmpv)
                Else
                    BuildSkipFolder(sPth)
                End If
                If iSts = 3 Then
                    Call InPause()
                End If
                If iSts = 2 Then Return
                If bSkipFolder = True Then
                    BuildSkipFolder(sPth)
                    Exit For
                End If
            Next
        Else
            ' this is a file
            TheStep()
            sInZName = sPth
            If sBaseNm <> "" Then
                sInZName = sInZName.Substring(sBaseNm.Length)
            End If
            BuildAddFile(sPth, sInZName)
        End If

    End Sub


    ' takes care of one and only one file
    Private Sub BuildAddFile(ByRef sPth As String, ByRef sInZName As String)
        Dim objZipEntry As ZipEntry
        Dim strmFile As FileStream = Nothing
        Try
            strmFile = File.OpenRead(sPth)
            strmFNfo.WriteLine(sInZName)
            objZipEntry = New ZipEntry(sInZName)
            objZipEntry.DateTime = DateTime.Now
            objZipEntry.Size = strmFile.Length
            lAddedBytes = lAddedBytes + strmFile.Length
            strmZipOutputStream.PutNextEntry(objZipEntry)
            StreamUtils.Copy(strmFile, strmZipOutputStream, abyBuffer)
        Catch
        Finally
            If Not strmFile Is Nothing Then strmFile.Close()
        End Try
    End Sub


    ' marks the folder to be skipped
    Private Sub BuildSkipFolder(ByRef sPath As String)
        Dim strmFile As FileStream = Nothing
        Dim sInZName As String
        Dim objZipEntry As ZipEntry
        Try
            sInZName = sPath & "\" & sFileToSkip
            If sBaseNm <> "" Then
                sInZName = sInZName.Substring(sBaseNm.Length)
            End If
            strmFNfo.WriteLine(sInZName)
            strmFile = File.OpenRead(sPthFSkip)
            objZipEntry = New ZipEntry(sInZName)
            objZipEntry.DateTime = DateTime.Now
            objZipEntry.Size = strmFile.Length
            strmZipOutputStream.PutNextEntry(objZipEntry)
            StreamUtils.Copy(strmFile, strmZipOutputStream, abyBuffer)
            UpdateStatistics(strmFile.Length)
            bSkipFolder = False
        Finally
            If Not strmFile Is Nothing Then strmFile.Close()
        End Try
    End Sub


    ' adds an entire folder to the zip
    Private Function BuildAPath(ByVal path As String) As Boolean
        If iSts = 2 Then Return False ' account for stop
        If IsExcl(path) = True Then Return False

        Dim dirInfo As New IO.DirectoryInfo(path)
        Dim sInZName As String
        Dim strmFile As FileStream = Nothing
        Dim lTmptOT As Long
        Dim fileObjects() As IO.FileSystemInfo

        Try
            fileObjects = dirInfo.GetFileSystemInfos()
        Catch
            Return False
        End Try

        With My.Computer.FileSystem
            lTmptOT = UBound(fileObjects) + 1
            TheProgInit(lTmptOT)
        End With


        For i As Integer = 0 To UBound(fileObjects)
            TheStep()
            sInZName = fileObjects(i).FullName
            If sBaseNm <> "" Then
                sInZName = sInZName.Substring(sBaseNm.Length)
            End If
            LogText("Processing " & sInZName & vbNewLine)
            If (fileObjects(i).Attributes And IO.FileAttributes.Directory) <> 0 Then
                LvlDeep = LvlDeep + 1
                strmFNfo.WriteLine(sInZName)
                Dim lTmpv As Long = Me.tsProgress.Value
                BuildAPath(fileObjects(i).FullName)
                TheReinit(lTmptOT, lTmpv)

                LvlDeep = LvlDeep - 1
            Else
                BuildAddFile(fileObjects(i).FullName, sInZName)
            End If

            If iSts = 3 Then
                Call InPause()
            End If
            If iSts = 2 Then Return False
            If bSkipFolder = True Then
                BuildSkipFolder(path)
                Exit For
            End If

            OnEachAddedItem()

        Next
        Return True
    End Function


#End Region


#Region "Updating"
    ' TODO  prevent failure because of restricted access 

    ' may be called as Thread new
    Private Sub UpdateZip_Async()
        UpdateZip()
        TheFinish()
    End Sub

    ' based on input and output paths, it updates an existing zip
    Private Function UpdateZip() As Boolean
        Dim sPthNfoTmp As String = ""
        Try
            ReDim abyBuffer(0 To 4095)
            ' have temp file in place
            sPthFSkip = Microsoft.VisualBasic.FileIO.SpecialDirectories.Temp & "\" & sFileToSkip
            strmFNfo = New System.IO.StreamWriter(sPthFSkip, False)
            strmFNfo.Close()

            ' effective update
            iItemsAdded = -1
            dateRefUpd = System.IO.File.GetLastWriteTime(sOutCl)
            zf = New ICSharpCode.SharpZipLib.Zip.ZipFile(sOutCl)
            AddHandler zf.EvStart, AddressOf EvZipStart
            AddHandler zf.EvStep, AddressOf EvZipStep
            AddHandler zf.EvEnd, AddressOf EvZipEnd
            AddHandler zf.EvOF_Fail, AddressOf EvOpenFailed


            ' move old nfo file to temp folder; get a new nfo
            sPthNfoTmp = _
                Microsoft.VisualBasic.FileIO.SpecialDirectories.Temp & _
                "\TempOldFile.nfo"
            If System.IO.File.Exists(sPthNfoTmp) Then System.IO.File.Delete(sPthNfoTmp)
            System.IO.File.Move(sOutCl & ".nfo", sPthNfoTmp)
            strmFNfoIn = New System.IO.StreamReader(sPthNfoTmp)
            strmFNfo = New System.IO.StreamWriter(sOutCl & ".nfo", False)


            GetNextNfoItem()
            UpdateAPath(sPathCl)

        Catch ex As System.Exception
            DoException(ex)
        Finally
            If Not zf Is Nothing Then
                ZIP_FinalUpd()
                zf.Close()
            End If
            If Not strmFNfo Is Nothing Then strmFNfo.Close()
            If Not strmFNfoIn Is Nothing Then strmFNfoIn.Close()
            ' delete temporary files
            If System.IO.File.Exists(sPthFSkip) Then System.IO.File.Delete(sPthFSkip)
            If System.IO.File.Exists(sPthNfoTmp) Then System.IO.File.Delete(sPthNfoTmp)
            ReDim abyBuffer(0 To 4)
        End Try
    End Function

    ' recursivelly updates a path based on current nfo entry nad provided path
    Private Function UpdateAPath(ByVal path As String) As Boolean
        If iSts = 2 Then Return False ' simply exit if a quit request is present
        ' path - has the current path as indicated by the input (real path)
        ' TODO account for empty folders; what to do with them? gost file?
        Dim lTmptOT As Long
        Dim sInZName As String
        Dim lTmpv As Long

        Try
            ' enshure canonical form for imput path (with ending backslash
            If path.Substring(path.Length - 1) <> "\" Then
                path = path & "\"
            End If
            ' attempt to get folder info for this path
            Dim dirInfo As New IO.DirectoryInfo(path)
            ' get the list of files and folders in a string array
            Dim fileObjects() As IO.FileSystemInfo = dirInfo.GetFileSystemInfos()

            ' retain total count for statistics
            lTmptOT = UBound(fileObjects) + 1
            ' progress bar indicates this folder
            TheProgInit(lTmptOT)

            ' check to see if both the nfo and the current path corespond
            ' if they don't it means that entire path was not present in previous
            ' versin of the file
            If (path.ToUpper = sUpdLn_P) Then
                ' we have a match

                ' get this name for zip of this current path
                sInZName = path
                If sBaseNm <> "" Then
                    sInZName = sInZName.Substring(sBaseNm.Length)
                End If
                ' and append it to current nfo
                strmFNfo.WriteLine(sInZName)

                ' have an array of boolean values to tell if coresponding item inside
                ' this folder has been added; init to false all of them
                Dim bAdded(0 To UBound(fileObjects)) As Boolean
                ' folowing loop may not be needed, as the array initialize to false
                For i As Integer = 0 To UBound(fileObjects)
                    bAdded(i) = False
                Next

                ' this will loop in all the elements present in the nfo file
                Do
                    ' get the name as it will be represented in zip
                    sInZName = sUpdLn_P & sUpdLn_N
                    If sBaseNm <> "" Then
                        sInZName = sInZName.Substring(sBaseNm.Length)
                    End If

                    ' iterate in all elements that are currently present
                    ' in the folder and try to match
                    For i As Integer = 0 To UBound(fileObjects)
                        If (fileObjects(i).Name.ToUpper = sUpdLn_N) Then
                            ' mark it as added
                            bAdded(i) = True
                            ' this doesn't really look good :) we get the same name but with case preserved
                            sInZName = fileObjects(i).FullName
                            If sBaseNm <> "" Then
                                sInZName = sInZName.Substring(sBaseNm.Length)
                            End If

                            ' append this thingy to current nfo
                            strmFNfo.WriteLine(sInZName)
                            ' found the coresponding entry into the nfo
                            ' check it's date
                            If fileObjects(i).LastWriteTime > dateRefUpd Then
                                ' the file is older' it has to be removed
                                LogText("Processing " & sInZName & vbNewLine)
                                ' this may be a file or folder
                                If (fileObjects(i).Attributes And FileAttributes.Directory) = 0 Then
                                    ' this is a file in zip
                                    ZIP_StartUpd()
                                    zf.Delete(sInZName)
                                    zf.Add(fileObjects(i).FullName, sInZName)
                                    ZIP_EndUpd()
                                    OnEachAddedItem()
                                Else
                                    lTmpv = Me.tsProgress.Value
                                    ' this is a folder in zip -> recurse
                                    UpdateAPath(fileObjects(i).Name)
                                    ' reset progress bar to indicate this folder
                                    TheReinit(lTmptOT, lTmpv)
                                End If
                                ' because we need to find a proper next item in following branch
                                ' we place this call here instead of after Next_NfoFile
                                GetNextNfoItem()
                            Else
                                ' this entire item is up to date
                                ' skip all entries in nfo that are kids of this
                                ' folder (if it's a folder)
                                If (fileObjects(i).Attributes And FileAttributes.Directory) = 0 Then
                                    GetNextNfoItem()
                                Else
                                    Dim sTmpPath As String = sUpdLn_P & sUpdLn_N
                                    Do
                                        GetNextNfoItem()
                                    Loop Until sUpdLn_P Like sTmpPath & "*" = False
                                    ' at the end of this loop the current nfo entry
                                    ' is either empty or points to next folder
                                End If
                            End If
                            ' we are ready to process next entry in nfo
                            GoTo Next_NfoFile
                        End If
                    Next i

                    ' this entry from the nfo has not been found on this folder
                    ' that means that the entry was erased from last update
                    ' TODO based on current settings we either remove that item or keep it
                    ZIP_StartUpd()
                    zf.Delete(sInZName)
                    ZIP_EndUpd()
                    OnEachAddedItem()

Next_NfoFile:
                    ' status check on each iteration for requested actions
                    If iSts = 3 Then
                        Call InPause()
                    End If
                    If iSts = 2 Then Return False
                    If bSkipFolder = True Then
                        UpdMarkAsSkipped(path)
                        Exit Try
                    End If
                    ' the entry was found (an sometimes updated); step to next entry
                    ' if new entry is part of this folder then continue
                    ' note that - when reaching the end of nfo - the path will become ""
                    ' thus breaking the loop
                Loop Until (path.ToUpper <> sUpdLn_P)


                ' all elements present in the nfo file have been iterated' if there are
                ' elements left in the folder, those are new and shall be appended
                For i As Integer = 0 To UBound(fileObjects)
                    If bAdded(i) = False Then
                        ' get the name as it will be represented in zip
                        sInZName = fileObjects(i).FullName
                        If sBaseNm <> "" Then
                            sInZName = sInZName.Substring(sBaseNm.Length)
                        End If
                        LogText("Processing " & sInZName & vbNewLine)

                        If (fileObjects(i).Attributes And IO.FileAttributes.Directory) <> 0 Then
                            lTmpv = Me.tsProgress.Value
                            ' dealing with a folder => recursivelly add path to the zip
                            RcrAddPath(fileObjects(i).FullName)
                            ' reset progress bar to indicate this folder
                            TheReinit(lTmptOT, lTmpv)
                        Else
                            ' dealing with a file
                            ZIP_StartUpd()
                            zf.Add(fileObjects(i).FullName, sInZName)
                            ZIP_EndUpd()
                            ' append it to current nfo
                            strmFNfo.WriteLine(sInZName)
                            OnEachAddedItem()
                        End If
                    End If

                    ' status check on each iteration for requested actions
                    If iSts = 3 Then
                        Call InPause()
                    End If
                    If iSts = 2 Then Return False
                    If bSkipFolder = True Then
                        UpdMarkAsSkipped(path)
                        Exit For
                    End If
                Next

            Else
                ' this entire path did not exist in previous version of the file
                ' we shall append it
                ' this item has not been added; do that now
                RcrAddPath(path)
            End If


        Catch ex As System.Exception
            DoException(ex)
        End Try

    End Function

    ' this function shall recursivelly add a path to the zip
    ' checks input against black list; appends the entry to new nfo
    Private Sub RcrAddPath(ByRef sPath As String)
        If iSts = 2 Then Return ' simply exit if a quit request is present
        ' TODO inherited from UpdateAPath
        Dim lTmptOT As Long
        Dim sInZName As String
        Dim lTmpv As Long

        If (IsExcl(sPath) = True) Then Return
        Try
            ' get this name for zip
            sInZName = sPath
            If sBaseNm <> "" Then
                sInZName = sInZName.Substring(sBaseNm.Length)
            End If
            ' and append it to current nfo
            strmFNfo.WriteLine(sInZName)

            ' attempt to get folder info for this path
            Dim dirInfo As New IO.DirectoryInfo(sPath)
            ' get the list of files and folders in a string array
            Dim fileObjects() As IO.FileSystemInfo = dirInfo.GetFileSystemInfos()
            ' retain total count for statistics
            lTmptOT = UBound(fileObjects) + 1
            ' progress bar indicates this folder
            TheProgInit(lTmptOT)


            ' iterate in all objects
            For i As Integer = 0 To UBound(fileObjects)
                ' get the name as it will be represented in zip
                sInZName = fileObjects(i).FullName
                If sBaseNm <> "" Then
                    sInZName = sInZName.Substring(sBaseNm.Length)
                End If
                LogText("Processing " & sInZName & vbNewLine)

                If (fileObjects(i).Attributes And IO.FileAttributes.Directory) <> 0 Then
                    lTmpv = Me.tsProgress.Value
                    ' dealing with a folder => recursivelly add path to the zip
                    RcrAddPath(fileObjects(i).FullName)
                    ' reset progress bar to indicate this folder
                    TheReinit(lTmptOT, lTmpv)
                Else
                    ' dealing with a file
                    ZIP_StartUpd()
                    zf.Add(fileObjects(i).FullName, sInZName)
                    ZIP_EndUpd()
                    OnEachAddedItem()
                End If

                ' status check on each iteration for requested actions
                If iSts = 3 Then
                    Call InPause()
                End If
                If iSts = 2 Then Return
                If bSkipFolder = True Then
                    UpdMarkAsSkipped(sPath)
                    Exit For
                End If
            Next

        Catch ex As System.Exception
            DoException(ex)
        End Try

    End Sub

    ' function to mark a folder as skipped
    Private Sub UpdMarkAsSkipped(ByRef sPath As String)
        Dim sInZName As String

        ' get this name for zip
        sInZName = sPath & "\" & sFileToSkip
        If sBaseNm <> "" Then
            sInZName = sInZName.Substring(sBaseNm.Length)
        End If

        ''MemoryDataSource is a Class Which was implemented from IStaticDataSource interface.
        Dim mByte(-1) As Byte
        Dim dtsource As New MemoryDataSource(mByte)

        ZIP_StartUpd()
        zf.Add(dtsource, sInZName)
        ZIP_EndUpd()
        bSkipFolder = False
    End Sub

#End Region


#Region "Others"


    Private Function IsExcl(ByRef sPath As String) As Boolean
        Dim i As Long
        Dim intPos As Integer
        Dim sNm As String
        intPos = sPath.LastIndexOfAny("\")
        intPos += 1

        sNm = sPath.Substring(intPos).ToUpper
        For i = 0 To UBound(sExclLst)
            If sExclLst(i).ToUpper = sNm Then
                Return True
            End If
        Next
        Return False
    End Function

    ' routine called in response to a pause event
    Private Sub InPause()
        InPause_C1()
        ChgSts("Paused")
        LogText(" pause acknowledged" & vbNewLine)
        While iSts = 3
            System.Threading.Thread.Sleep(100)
        End While
        InPause_C()
        ChgSts(sCrtMode)
    End Sub

    ' called on each file that was added; may be usefull to display the speed
    Private Sub UpdateStatistics(ByVal lFileSz As Int64)

    End Sub

    ' in response to a go / stop, this changes the UI
    Private Sub LockForRun(ByVal bRun As Boolean)

        With Me
            .bPause.Enabled = bRun
            .bSkipFile.Enabled = bRun
            .bSkipDir.Enabled = bRun
            bRun = Not bRun
            .txIdlePics.Enabled = bRun
            .txIn.Enabled = bRun
            .txOut.Enabled = bRun
            .txStrip.Enabled = bRun
            .cbLvlCp.Enabled = bRun
            .bBrwIn.Enabled = bRun
            .bBrwOut.Enabled = bRun
            'If Not bBrw Is Nothing Then
            '    bBrw.TreeView.Enabled = bRun
            'End If
        End With
    End Sub

    ' simply reads a new line from nfo file and divides it into patha and name
    Private Sub GetNextNfoItem()
        Dim i As Integer

        If strmFNfoIn.EndOfStream = True Then
            sUpdLn_N = ""
            sUpdLn_P = sBaseNm
            Return
        End If

        sUpdLn_P = strmFNfoIn.ReadLine().ToUpper
        i = sUpdLn_P.LastIndexOf("\")
        If i = -1 Then
            sUpdLn_N = sUpdLn_P
            sUpdLn_P = sBaseNm
        Else
            sUpdLn_N = sUpdLn_P.Substring(i + 1)
            sUpdLn_P = sBaseNm & sUpdLn_P.Substring(0, i + 1)
        End If
    End Sub

    ' generic exception handler
    Private Sub DoException(ByRef ex As System.Exception)
        LogText(" --------------- EXCEPTION ---------------" & vbNewLine)
        LogText(ex.ToString & vbNewLine)
        LogText(" ^^^^^^^^^^^^^^^ EXCEPTION ^^^^^^^^^^^^^^^" & vbNewLine)
    End Sub

    ' groups several files added in one update
    Private Sub ZIP_StartUpd()
        If iItemsAdded = -1 Then
            zf.BeginUpdate()
            iItemsAdded = 0
        ElseIf iItemsAdded > iUpdCnt Then
            Try
                przSts = tslSts.Text
                ChgSts("Writing zip")
                zf.CommitUpdate()
                ChgSts(przSts)
            Catch ex As System.Exception
                LogText("ERROR!!! Can't add files!!!" & vbNewLine & _
                    "Quit any programs that may be using the files and try again!" & vbNewLine)
                DoException(ex)
            End Try
            zf.BeginUpdate()
            iItemsAdded = 0
        Else
            iItemsAdded = iItemsAdded + 1
        End If
    End Sub


    Private Sub ZIP_EndUpd()

    End Sub


    Private Sub ZIP_FinalUpd()
        If iItemsAdded > 0 Then
            przSts = tslSts.Text
            ChgSts("Writing zip")
            zf.CommitUpdate()
            ChgSts(przSts)
        End If
    End Sub



    Dim przProgV As Long
    Dim przProgMax As Long
    Dim przSts As String

    Private Sub EvZipStart(ByVal sender As Object, ByVal e As ZipEventArgs)
        przProgV = Me.tsProgress.Value
        przProgMax = Me.tsProgress.Maximum
        TheProgInit(e.iNr)
    End Sub

    Private Sub EvZipStep(ByVal sender As Object, ByVal e As ZipEventArgs)
        TheValStep(e.iNr)
    End Sub

    Private Sub EvZipEnd(ByVal sender As Object, ByVal e As ZipEventArgs)
        TheReinit(przProgMax, przProgV)
    End Sub

    Private Sub EvOpenFailed(ByVal sender As Object, ByVal e As ZipEventArgs)
        LogText("ERROR!!! The file: " & e.sStr & vbNewLine & _
            "could not be opened durring zip update! Quit any running program" & vbNewLine & _
            "that may be using this file and retry." & vbNewLine)
    End Sub


#End Region


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        If iSts = 0 Then ' if idle
             ' go build / update

            ' aquire input values
            sPathCl = Me.txIn.Text
            sOutCl = Me.txOut.Text
            Try
                iDoEvTime = CLng(Me.txIdlePics.Text)
            Catch ex As Exception
                MsgBox("The value for idle cycles is non-numeric", MsgBoxStyle.ApplicationModal Or MsgBoxStyle.Information)
                Return
            End Try
            If System.IO.Directory.Exists(sPathCl) = False Then
                MsgBox("Specified folder for input does not exists", MsgBoxStyle.ApplicationModal Or MsgBoxStyle.Information)
                Return
            End If
            iCompressLvl = Me.cbLvlCp.SelectedIndex + 1
            sBaseNm = Me.txStrip.Text
            If (sBaseNm <> "") Then
                If Not sBaseNm Like "*\" Then
                    sBaseNm = sBaseNm & "\"
                End If
                If (Me.txIn.Text.IndexOf(sBaseNm) <> 0) Then
                    MsgBox("The strip must be part of input path", MsgBoxStyle.ApplicationModal Or MsgBoxStyle.Information)
                    Return
                End If
            End If


            ' general preparations for run
            Me.bGoStop.Text = "Stop"
            LogText("Starting the process ..." & vbNewLine)
            LockForRun(True)
            dateSt = DateTime.Now
            Me.tslStartTime.Text = dateSt.ToString("hh:mm:ss")
            lAddedBytes = 0
            iTyBytes = 0
            Me.tbsByCntLbl.Text = "Bytes added:"
            Me.tsProgress.Minimum = 0


            iSts = 1

           ' the run
            If System.IO.File.Exists(sOutCl) = True Then
                If (System.IO.File.Exists(sOutCl & ".nfo")) Then
                    System.IO.File.Delete(sOutCl & ".nfo")
                End If
                System.IO.File.Delete(sOutCl)
            End If
            thWork = New System.Threading.Thread(AddressOf BuildNewZip_Async)
            thWork.Start()


        Else ' stop now ...
            LogText("Stopped. Waiting acknowledge ..." & vbNewLine)
            Me.bGoStop.Enabled = False
            Me.bGoStop.Text = "Canceling ..."
            iSts = 2
        End If
    End Sub
End Class

Public Class MemoryDataSource
    Implements IStaticDataSource
    Public StreamData() As Byte

    Public Function GetSource() As System.IO.Stream Implements ICSharpCode.SharpZipLib.Zip.IStaticDataSource.GetSource
        Dim ms As MemoryStream = New MemoryStream(StreamData)
        ms.Position = 0
        Return ms
    End Function

    Sub New(ByVal Strdata() As Byte)
        Me.StreamData = Strdata
    End Sub
End Class